package com.hqd.ch03.v40.web.servlet.view;

import com.hqd.ch03.utils.CollectionUtils;
import com.hqd.ch03.v40.aware.BeanNameAware;
import com.hqd.ch03.v40.web.servlet.View;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

public abstract class AbstractView implements View, BeanNameAware {
    /**
     * 默认编码格式
     */
    public static final String DEFAULT_CONTENT_TYPE = "text/html;charset=ISO-8859-1";

    private static final int OUTPUT_BYTE_ARRAY_INITIAL_SIZE = 4096;
    /**
     * 静态属性，共享、不可变的属性放在此处
     */
    private final Map<String, Object> staticAttributes = new LinkedHashMap<>();
    private String contentType = DEFAULT_CONTENT_TYPE;
    /**
     * 是否暴露内部对象
     */
    private String requestContextAttribute;
    private boolean exposePathVariables = true;

    /**
     * 是否暴露bean对象
     */
    private boolean exposeContextBeansAsAttributes = false;

    private Set<String> exposedContextBeanNames;

    private String beanName;

    @Override
    public void render(Map<String, ?> model, HttpServletRequest request,
                       HttpServletResponse response) throws Exception {

        Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
        prepareResponse(request, response);
        renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
    }

    protected void exposeModelAsRequestAttributes(Map<String, Object> model,
                                                  HttpServletRequest request) throws Exception {
        model.forEach((name, value) -> {
            if (value != null) {
                request.setAttribute(name, value);
            } else {
                request.removeAttribute(name);
            }
        });
    }

    protected abstract void renderMergedOutputModel(
            Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception;

    protected HttpServletRequest getRequestToExpose(HttpServletRequest originalRequest) {
        return originalRequest;
    }

    protected Map<String, Object> createMergedOutputModel(Map<String, ?> model,
                                                          HttpServletRequest request, HttpServletResponse response) {

        /**
         * 这里获取路径上的参数，并没有进行支持
         */
        Map<String, Object> pathVars = (this.exposePathVariables ?
                (Map<String, Object>) request.getAttribute(View.PATH_VARIABLES) : null);

        int size = this.staticAttributes.size();
        size += (model != null ? model.size() : 0);
        size += (pathVars != null ? pathVars.size() : 0);

        Map<String, Object> mergedModel = CollectionUtils.newLinkedHashMap(size);
        mergedModel.putAll(this.staticAttributes);
        if (pathVars != null) {
            mergedModel.putAll(pathVars);
        }
        if (model != null) {
            mergedModel.putAll(model);
        }

        // Expose RequestContext?
        if (this.requestContextAttribute != null) {
            //   mergedModel.put(this.requestContextAttribute, createRequestContext(request, response, mergedModel));
        }

        return mergedModel;
    }

    protected void prepareResponse(HttpServletRequest request, HttpServletResponse response) {
        /**
         * 是否生成下载内容
         */
        if (generatesDownloadContent()) {
            /**
             * 控制缓存
             */
            response.setHeader("Pragma", "private");
            response.setHeader("Cache-Control", "private, must-revalidate");
        }
    }

    public void addStaticAttribute(String name, Object value) {
        this.staticAttributes.put(name, value);
    }

    public void setContentType(String contentType) {
        this.contentType = contentType;
    }

    public void setAttributesMap(Map<String, ?> attributes) {
        if (attributes != null) {
            attributes.forEach(this::addStaticAttribute);
        }
    }

    public void setExposePathVariables(boolean exposePathVariables) {
        this.exposePathVariables = exposePathVariables;
    }

    public void setExposeContextBeansAsAttributes(boolean exposeContextBeansAsAttributes) {
        this.exposeContextBeansAsAttributes = exposeContextBeansAsAttributes;
    }

    public void setExposedContextBeanNames(String... exposedContextBeanNames) {
        this.exposedContextBeanNames = new HashSet<>(Arrays.asList(exposedContextBeanNames));
    }

    public void setRequestContextAttribute(String requestContextAttribute) {
        this.requestContextAttribute = requestContextAttribute;
    }

    protected boolean generatesDownloadContent() {
        return false;
    }

    public String getBeanName() {
        return this.beanName;
    }

    @Override
    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }
}
